from formula import *
from gene import *
from operator import itemgetter
import sys
import numpy as np
import datetime


class Evolution(object):
	def __init__(self, signal):
		self.date = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
		self.logFile = "log01.txt"
		self.signal = np.array(signal)
		self.meanSignal = self.signal.mean()
		self.varSignal = self.signal.var()
		self.lenSignal = len(signal)

		self.nKeeped = 200
		self.nReproduced = self.nKeeped*2
		self.nEasyMutated = self.nKeeped*2
		self.nAdvancedMutated = self.nKeeped*2
		self.nAdvancedMutated = self.nKeeped
		self.nDualMutated = self.nKeeped*1
		#self.nDualMutated = 0
		self.idIteration = 0
		self.maxIteration = 3
		self.deltaRelativeDistance = 0.001
		self._generateInitialPopulation()
		self.pause = True

	def writeIterationInLogFile(self):
		f = open(self.logFile, 'a')
		now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
		data = [self.date, now, self.lenSignal, self.meanSignal, self.varSignal, self.nKeeped, self.nReproduced, self.nEasyMutated, self.nAdvancedMutated, self.nDualMutated, self.maxIteration, self.deltaRelativeDistance, self.idIteration, self.getDistanceToSignal(), self.getRelativeDistanceToSignal(), self.getFormula()]
		data = map(str, data)
		line = '\t'.join(data) + '\n'
		f.write(line)
		f.close()

	def _generateInitialPopulation(self):
		temp = Gene(self.signal)
		maxDistance = temp.maxDistanceToSignal
		self.genes = [(Gene(self.signal), maxDistance) for x in xrange(self.nKeeped)]

	def startInThread(self):
		self.pause = False
		for x in xrange(self.idIteration, self.maxIteration):
			if not self.pause:
				if self.getRelativeDistanceToSignal() < self.deltaRelativeDistance:
					break
				self._doOneIteration()

	def _doOneIteration(self):
		print "------------------------------------------------"
		print "Iteration", self.idIteration, "stated..."
		newGene = None
		for x in xrange(self.nReproduced):
			distance = -1
			while distance == -1:
				randomId = self.__randomGeneId()
				newGene = self.genes[randomId][0].clone()
				newGene.change()
				distance = newGene.getDistanceToSignal()
			self.genes.append((newGene, distance))
		print "reprod ok"
		for x in xrange(self.nEasyMutated):
			distance = -1
			while distance == -1:
				randomId = self.__randomGeneId()
				newGene = self.genes[randomId][0].clone()
				newGene.easyMutate()
				distance = newGene.getDistanceToSignal()
			self.genes.append((newGene, distance))
		print "easy ok"
		for x in xrange(self.nAdvancedMutated):
			distance = -1
			while distance == -1:
				randomId = self.__randomGeneId()
				newGene = self.genes[randomId][0].clone()
				newGene.advanceMutate()
				distance = newGene.getDistanceToSignal()
			self.genes.append((newGene, distance))
		print "advanced ok"
		for x in xrange(self.nDualMutated):
			distance = -1
			while distance == -1:
				father = self.genes[self.__randomGeneId()][0].clone()
				mother = self.genes[self.__randomGeneId()][0]
				newGene = father.dualMutation(mother)
				distance = newGene.getDistanceToSignal()
			self.genes.append((newGene, distance))
		print "dual ok"
		self.__sortGenesAndKeepBest()
		print "Absolute distance:", self.getDistanceToSignal()
		print "Relative distance:", self.getRelativeDistanceToSignal()
		print "Iteration", self.idIteration, "done"
		self.writeIterationInLogFile()
		self.idIteration += 1

	def __randomGeneId(self):
		idGene = (exp(random())-1)/(exp(1)-1) * (self.nKeeped-1)
		return int(round(idGene))

	def __sortGenesAndKeepBest(self):
		self.genes = sorted(self.genes, key=itemgetter(1))
		self.genes = self.genes[:self.nKeeped]

	def pause(self):
		self.pause = True

	def terminate(self):
		self.pause()

	def getBestGene(self):
		pass

	def getDistanceToSignal(self):
		return self.genes[0][1]

	def getRelativeDistanceToSignal(self):
		return self.getBestGene().getRelativeDistanceToSignal()

	def getBestGene(self):
		return self.genes[0][0]

	def getEstimatedSignal(self):
		return self.getBestGene().getEstimatedSignal()

	def getFormula(self):
		return self.getBestGene().formula

if __name__ == '__main__':
	seed(5)
	signal = [10,5,10,15,10,5,10]
	signal = [
8, -1, -7, -3, 6, 13, 11, 2, -6, -5, 3, 11, 12, 4, -5, -7, 0, 10, 13, 7, -2, -7, -2, 7, 13,
10, 0, -7, -4, 5, 12, 11, 3, -5, -6, 2, 11, 13, 6, -4, -7, -1, 9, 13, 8, -1, -7, -3, 6, 13,
10, 1, -6, -5, 3, 12, 12, 4, -5, -6, 0, 10, 13, 7, -3, -7, -2, 7, 13, 9, 0, -7, -4, 5, 12,
11, 3, -6, -6, 2, 11, 13, 6, -4, -7, -1, 9, 13, 8, -1, -7, -3, 6, 13, 10, 1, -6, -5, 3, 12
]

	evolution = Evolution(signal)
	evolution.startInThread()
	print "Absolute distance:", evolution.getDistanceToSignal()
	print "Relative distance:", evolution.getRelativeDistanceToSignal()
	print "Estimated signal:", evolution.getEstimatedSignal()
	print "Initial signal:", signal
	print "Estimated rounded signal:", map(int, map(round, evolution.getEstimatedSignal()))
	print "Formula:", evolution.getFormula()

